﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace BobcatWiiLib
{
    /// <summary>
    /// This form gathers the data needed for a four-point calibration.
    /// 
    /// It shows a cross-hair point in turn at each of the four corners of the screen. You 
    /// click on it with the LED source and when you release the click we store the coordinates
    /// from the Wiimote alongside the screen coordinates. 
    /// 
    /// The caller can retrieve these pairs of points and use them to calibrate the screen 
    /// coords to the wiimote coords.
    /// </summary>
    partial class CalibrationForm : Form
    {
        #region Public types and properties

        /// <summary>
        /// Holds a point from the wiimote camera alongside the point in screen coordinates.
        /// </summary>
        public class CoordinatePair
        {
            public double wiimoteX;
            public double wiimoteY;
            public double screenX;
            public double screenY;
        }

        #endregion

        #region Public methods

        /// <summary>
        /// Constructor.
        /// </summary>
        public CalibrationForm(WiimoteWrapper wiimote, string strImageFilename)
        {
            m_Wiimote = wiimote;
            InitializeComponent();

            if (strImageFilename != "")
            {
                Image img = Image.FromFile(strImageFilename);
                BackgroundImage = img;
            }
        }

        
        /// <summary>
        /// Returns the collection of coordinate pairs, i.e. pairs of wiimote and corresponding 
        /// screen coordinates.
        /// </summary>
        public IList<CoordinatePair> getCoordinatePairs()
        {
            return m_CoordinatePairs;
        }

        #endregion

        #region Private helper functions

        /// <summary>
        /// Form Load event.
        /// </summary>
        private void CalibrationForm_Load(object sender, EventArgs e)
        {
            setTargetPositions();

            // We show the first calibration point...
            moveToNextCalibrationPoint();
            m_Wiimote.IRStatusChangedEvent += onIRStateChange;

            // We set up the camera viwer control...
            ctrlWiimoteViewer.initialize(m_Wiimote);
        }


        /// <summary>
        /// Called when an IR point is turned on or off.
        /// </summary>
        private void onIRStateChange(object sender, WiimoteWrapper.IRPointArgs args)
        {
            // If we are in "preview mode" we don't respond to the points...
            if (radioPreview.Checked == true)
            {
                return;
            }

            // Have we got a click on for dot 0? We want the state to be false, i.e. off as
            // this will give time for some smoothing to occur.
            if (args.Source != 0 || args.Active != false)
            {
                return;
            }

            // We've got a click, so we store the point...
            storePoint(args);
        }


        /// <summary>
        /// We store the wiimote point and the screen point of the center of the target.
        /// </summary>
        /// <param name="args"></param>
        void storePoint(WiimoteWrapper.IRPointArgs args)
        {
            CoordinatePair coordinatePair = new CoordinatePair();

            // We find the center of the target. We need to convert this to 
            // screen coordinates...
            Point clientPoint = new Point(m_CurrentTarget.Left + 10, m_CurrentTarget.Top + 10);
            Point screenPoint = PointToScreen(clientPoint);
            coordinatePair.screenX = screenPoint.X;
            coordinatePair.screenY = screenPoint.Y;

            // We find the wiimote point...
            coordinatePair.wiimoteX = args.PositionX;
            coordinatePair.wiimoteY = args.PositionY;

            m_CoordinatePairs.Add(coordinatePair);

            // We show the next calibration point...
            moveToNextCalibrationPoint();
        }


        /// <summary>
        /// We show the calibration point that you should be clicking.
        /// </summary>
        private void moveToNextCalibrationPoint()
        {
            ctrlTarget1.Visible = false;
            ctrlTarget2.Visible = false;
            ctrlTarget3.Visible = false;
            ctrlTarget4.Visible = false;

            // We see which calibration points we have shown, and we show the next one...
            switch (m_CoordinatePairs.Count)
            {
                case 0:
                    m_CurrentTarget = ctrlTarget1;
                    break;
                case 1:
                    m_CurrentTarget = ctrlTarget2;
                    break;
                case 2:
                    m_CurrentTarget = ctrlTarget3;
                    break;
                case 3:
                    m_CurrentTarget = ctrlTarget4;
                    break;
                case 4:
                    // We've got all the points we need, so we close form...
                    m_Wiimote.IRStatusChangedEvent -= onIRStateChange;
                    Close();
                    break;
            }
            m_CurrentTarget.Visible = true;
        }

        /// <summary>
        /// Called when the form is resized. We make sure that the calibration targets are shown in the
        /// correct places.
        /// </summary>
        private void CalibrationForm_Resize(object sender, EventArgs e)
        {
            setTargetPositions();
        }

        /// <summary>
        /// We position the targets in the four corners of the screen.
        /// </summary>
        private void setTargetPositions()
        {
            ctrlTarget1.Left = TARGET_MARGIN;
            ctrlTarget1.Top = TARGET_MARGIN;

            ctrlTarget2.Left = ClientSize.Width - TARGET_MARGIN - TARGET_SIZE;
            ctrlTarget2.Top = TARGET_MARGIN;

            ctrlTarget3.Left = ClientSize.Width - TARGET_MARGIN - TARGET_SIZE;
            ctrlTarget3.Top = ClientSize.Height - TARGET_MARGIN - TARGET_SIZE;

            ctrlTarget4.Left = TARGET_MARGIN;
            ctrlTarget4.Top = ClientSize.Height - TARGET_MARGIN - TARGET_SIZE;
        }

        /// <summary>
        /// Called when the 'preview' radio button is clicked.
        /// </summary>
        private void radioPreview_Click(object sender, EventArgs e)
        {
            // We show all the targets...
            ctrlTarget1.Visible = true;
            ctrlTarget2.Visible = true;
            ctrlTarget3.Visible = true;
            ctrlTarget4.Visible = true;
        }

        /// <summary>
        /// Called when the 'calibrate' radio button is clicked.
        /// </summary>
        private void radioCalibrate_Click(object sender, EventArgs e)
        {
            // We reset the calibration and show the first target...
            m_CoordinatePairs.Clear();
            moveToNextCalibrationPoint();
        }

        #endregion

        #region Private data

        private const int TARGET_MARGIN = 40; // How far from the screen edge the targets appear
        private const int TARGET_SIZE = 20;

        private WiimoteWrapper m_Wiimote;
        private IList<CoordinatePair> m_CoordinatePairs = new List<CoordinatePair>();
        private CalibrationTarget m_CurrentTarget = null;

        #endregion

    }
}
